//
//  SpriterNode.cpp
//  Tank
//
//  Created by Young Peter on 9/1/12.
//  http://weibo.com/young40
//  revised by qibinyi@gmail.com,  20130111
//
//

#include "SpriterNode.h"

#include "lib_arithmetic.h"

USING_NS_CC;
using namespace std;

SpriterNode::SpriterNode()
{
    _playbackSpeed = 1.0;
 
    _spriteNodes = new CCArray();
    _dict_moduleMapper = new CCDictionary();

}

SpriterNode::~SpriterNode()
{
    unschedule(schedule_selector(SpriterNode::update));
    
    if (_spriteNodes)
    {
        _spriteNodes->removeAllObjects();
        _spriteNodes->release();
        _spriteNodes = NULL;
    }
    
    CC_SAFE_DELETE(_dict_moduleMapper);
    
//    CCLog("~SpriterNode release");
}



SpriterNode* SpriterNode::create(const char *scmlFile, const char *plistFile)
{
    SpriterNode * pNode = new SpriterNode();
    
    // create anim
    pNode->cAnim = cAnimation::create( scmlFile , plistFile ) ;
    
    // get png file name
    string texturePath("");
    // build texture path by replacing file extension
    texturePath = plistFile;
    
    // remove .xxx
    size_t startPos = texturePath.find_last_of(".");
    texturePath = texturePath.erase(startPos);
    
    // append .png
    texturePath = texturePath.append(".png");
    
    
    
    pNode->_batchNode = CCSpriteBatchNode::create(texturePath.c_str());
    pNode->addChild(pNode->_batchNode);
    
    pNode->setContentSize(CCSizeZero);
    
    
    
    
    
    if (pNode )
    {
        pNode->autorelease();
        return pNode;
    }
    
    CC_SAFE_DELETE(pNode);
    return NULL;
}

void SpriterNode::setAnimation(cAnimation* anim) {
    
    this->cAnim = anim ;
}



void SpriterNode::runAnimation(const char *animation , bool bLoop )
{
    unschedule(schedule_selector(SpriterNode::update));
    
    _duration = 0;
    _frameIndex = 0;
    _bLoop = bLoop ;
    _bFinished = false;
    _bRectNeedUpdate = true;
    
    for (int i=0;i<4;i++ ) {
        _rect[i] = RECT_DEFAULT_VALUE;
    }
    
    
    _curAnimation = (SpriterAnimation*)(  cAnim->getAnimAnimations() ->objectForKey(animation) );
    
    _curKeyFrame = (SpriterMainlineKey*)_curAnimation->getMainline()->objectAtIndex(_frameIndex);
    _nextKeyFrame = (SpriterMainlineKey*)_curAnimation->getMainline()->objectAtIndex((_frameIndex+1)%_curAnimation->getMainline()->count());
    
    
    schedule(schedule_selector(SpriterNode::update));
}

bool SpriterNode::isAnimFinished( ) {
    
    return _bFinished ;
}

bool SpriterNode::isCollided( float x , float y) {
    
    return _rect[0]<=x && x<= _rect[2]  && _rect[1]<=y && y<= _rect[3];
    
}

void SpriterNode::update(float dt)
{
 
    
    _duration += dt;
    
    int milliseconds = _duration * 10000 * _playbackSpeed;
    int startTime = _curKeyFrame->getStartsAt();
    int endTime = _nextKeyFrame->getStartsAt();
    
    if (endTime == 0.0f)
    {
        endTime = _curAnimation->getDuration();
    }
    
    if (milliseconds > endTime)
    {
        _curKeyFrame = _nextKeyFrame;
        
        //不是最后一贞，或者是 循环动画， _frameIndex++
        if (_frameIndex< _curAnimation->getMainline()->count()-1 || _bLoop ) {
            _frameIndex = (_frameIndex+1)%_curAnimation->getMainline()->count();
            _bRectNeedUpdate = true;
        }
        
        // 不是循环动画, 且当前侦已经是最后一贞，判定动画结束
        else if ( _frameIndex >= _curAnimation->getMainline()->count()-1 && !_bLoop ) {
            _bFinished = true;
        }
        
        //下一侦不是最后一贞，或者是 循环动画 ,循环取下一侦
        if (_frameIndex+1 < _curAnimation->getMainline()->count()-1  || _bLoop )
            _nextKeyFrame = (SpriterMainlineKey*)_curAnimation->getMainline()->objectAtIndex((_frameIndex+1)%_curAnimation->getMainline()->count());
        //否是，下一侦和当前侦相同
        else {
            _nextKeyFrame = _curKeyFrame;
        }
        
        startTime = _curKeyFrame->getStartsAt();
        endTime = _nextKeyFrame->getStartsAt();
    }
    if (milliseconds > _curAnimation->getDuration())
    {
        _duration -= milliseconds * 0.0001/_playbackSpeed;
        milliseconds -= 10000 * _playbackSpeed;
    }
    
    for (int i=0; i<_spriteNodes->count(); i++)
    {
        ((CCSprite*)_spriteNodes->objectAtIndex(i))->setVisible(false);
    }
    
    // interpolation
    double interpolationFactor = ((milliseconds - startTime)/(1.0*(endTime-startTime)));
    
    //初始化
    float tmp_rect[4] = { RECT_DEFAULT_VALUE, RECT_DEFAULT_VALUE, RECT_DEFAULT_VALUE, RECT_DEFAULT_VALUE };
    
    for (int i=0; i<_curKeyFrame->getObjectRefs()->count(); i++)
    {
        SpriterObjectRef *curObjectRef = (SpriterObjectRef*)_curKeyFrame->getObjectRefs()->objectAtIndex(i);
        
        
        SpriterTimeline *objectTimeline = (SpriterTimeline*)_curAnimation->getTimelines()->objectAtIndex(curObjectRef->getTimelineId());
        
        SpriterTimelineKey *curTimelineKey = (SpriterTimelineKey*)objectTimeline->getKeys()->objectAtIndex(curObjectRef->getTimelineKey());
        
        
        //只需取 下一贞的 任意 ObjectRef
        SpriterObjectRef *nextObjectRef = (SpriterObjectRef*)_nextKeyFrame->getObjectRefs()->objectAtIndex(0);
        SpriterTimelineKey *nextTimelineKey = (SpriterTimelineKey*)objectTimeline->getKeys()->objectAtIndex(nextObjectRef->getTimelineKey());
        
        const char * _key = CCString::createWithFormat("%d-%d", curTimelineKey->getFolderId(), curTimelineKey->getFileId())->getCString();
        //CCLog("%d %d %s" ,  i,  _curKeyFrame->getObjectRefs()->count() ,  _key );
        const char *displayFrameName = ((CCString*) cAnim->getAnimFiles()->objectForKey(_key))->getCString();
        // check mapper module
        displayFrameName = doModuleMapper(  _curAnimation->getName() ,displayFrameName ) ;
        
        CCSprite *sprite;
        
        if (i >= _spriteNodes->count())
        {

            {
                sprite = CCSprite::createWithSpriteFrameName(displayFrameName);
                _batchNode->addChild(sprite);
            }

            
            _spriteNodes->addObject(sprite);
        }
        else
        {
            sprite = (CCSprite*)_spriteNodes->objectAtIndex(i);

            {
//                CCLog(" %s %d" , displayFrameName , CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(displayFrameName) );
                sprite->setDisplayFrame( CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(displayFrameName));
            }
        }
        
        sprite->setVisible(true);
        
        sprite->setPosition(CCPointMake(
                interpolate(curTimelineKey->getPostion().x, nextTimelineKey->getPostion().x, interpolationFactor),
                interpolate(curTimelineKey->getPostion().y, nextTimelineKey->getPostion().y, interpolationFactor)));
        
        sprite->setAnchorPoint(CCPointMake(
                interpolate(curTimelineKey->getAnchorPoint().x, nextTimelineKey->getAnchorPoint().x, interpolationFactor),
                interpolate(curTimelineKey->getAnchorPoint().y, nextTimelineKey->getAnchorPoint().y, interpolationFactor)));
        
        sprite->setScaleX(interpolate(curTimelineKey->getScaleX(), nextTimelineKey->getScaleX(), interpolationFactor));
        sprite->setScaleY(interpolate(curTimelineKey->getScaleY(), nextTimelineKey->getScaleY(), interpolationFactor));
        
        double nextRotation = nextTimelineKey->getRotation();
        double curRotation = curTimelineKey->getRotation();
        
        if (curTimelineKey->getSpin() == 1 && (nextRotation - curRotation) < 0)
        {
            nextRotation += 360;
        }
        else if(curTimelineKey->getSpin() == -1 && (nextRotation - curRotation ) >0)
        {
            nextRotation -= 360;
        }
        
        // check to flip
        if(_isFlipX)
        {
            // position
            sprite->setPosition(CCPointMake(-sprite->getPosition().x, sprite->getPosition().y));
            
            // scale
            sprite->setScaleX(-sprite->getScaleX());
            
            // rotation
            nextRotation *= -1;
            curRotation *= -1;
        }
        
        sprite->setRotation( - interpolate(curRotation, nextRotation, interpolationFactor) );
        
        
        // check bound
//        ccV3F_C4B_T2F_Quad m_sQuad = sprite->getQuad();
//        CCPoint vertices[4]={
//            ccp(m_sQuad.tl.vertices.x,m_sQuad.tl.vertices.y),
//            ccp(m_sQuad.bl.vertices.x,m_sQuad.bl.vertices.y),
//            ccp(m_sQuad.br.vertices.x,m_sQuad.br.vertices.y),
//            ccp(m_sQuad.tr.vertices.x,m_sQuad.tr.vertices.y),
//        };
        
        //if (i!=0) continue;
        
        if (_bRectNeedUpdate) {
            float x = sprite->getPosition().x* (_isFlipX?-1:1) ;
            float y = sprite->getPosition().y;
            float width = sprite->getContentSize().width;
            float height = sprite->getContentSize().height;
            
            float ap_x = sprite->getAnchorPoint().x ;
            float ap_y = sprite->getAnchorPoint().y ;
            
            //不知道为什么
            //取消flipx影响， 然后从 笛卡儿坐标系，换成 openGL 坐标系
            float angle = sprite->getRotation()* (!_isFlipX?-1:1) ;
            
            float scale_x = sprite->getScaleX()* (_isFlipX? -1:1) ;
            float scale_y = sprite->getScaleY();
            
            float tmp_x =   ( 0-ap_x)*width*scale_x ;//*
            float tmp_y =   (0-ap_y)*height*scale_y ;//*
            float x0= x+ tmp_x*cosf(CC_DEGREES_TO_RADIANS(angle))-tmp_y*sinf(CC_DEGREES_TO_RADIANS(angle));
            float y0= y+ tmp_y*cosf(CC_DEGREES_TO_RADIANS(angle))+tmp_x*sinf(CC_DEGREES_TO_RADIANS(angle));
            
            tmp_x =   ( 0-ap_x)*width*scale_x;//*cosf ( CC_DEGREES_TO_RADIANS( angle ) );
            tmp_y =   (1-ap_y)*height*scale_y;//*sinf( CC_DEGREES_TO_RADIANS( angle ) ); //
            float x1= x + tmp_x*cosf(CC_DEGREES_TO_RADIANS(angle))-tmp_y*sinf(CC_DEGREES_TO_RADIANS(angle));
            float y1= y + tmp_y*cosf(CC_DEGREES_TO_RADIANS(angle))+tmp_x*sinf(CC_DEGREES_TO_RADIANS(angle));
            
            tmp_x =   ( 1-ap_x)*width*scale_x;//*cosf ( CC_DEGREES_TO_RADIANS( angle ) ); //
            tmp_y =   (0-ap_y)*height*scale_y;//*sinf( CC_DEGREES_TO_RADIANS( angle ) );
            float x2= x + tmp_x*cosf(CC_DEGREES_TO_RADIANS(angle))-tmp_y*sinf(CC_DEGREES_TO_RADIANS(angle));
            float y2= y + tmp_y*cosf(CC_DEGREES_TO_RADIANS(angle))+tmp_x*sinf(CC_DEGREES_TO_RADIANS(angle));
            
            tmp_x =   ( 1-ap_x)*width*scale_x;//*cosf ( CC_DEGREES_TO_RADIANS( angle ) );  //
            tmp_y =   (1-ap_y)*height*scale_y;//*sinf( CC_DEGREES_TO_RADIANS( angle ) );  //
            float x3= x + tmp_x*cosf(CC_DEGREES_TO_RADIANS(angle))-tmp_y*sinf(CC_DEGREES_TO_RADIANS(angle));
            float y3= y + tmp_y*cosf(CC_DEGREES_TO_RADIANS(angle))+tmp_x*sinf(CC_DEGREES_TO_RADIANS(angle));
            
            if (_isFlipX) {
                x0=-x0;
                x1=-x1;
                x2=-x2;
                x3=-x3;
            }
            
            float min_x = MIN(  MIN( MIN(x0, x1) , x2) , x3) ;
            float min_y = MIN(  MIN( MIN(y0, y1) , y2) , y3) ;
            float max_x = MAX(  MAX( MAX(x0, x1) , x2) , x3) ;
            float max_y = MAX(  MAX( MAX(y0, y1) , y2) , y3) ;
            
            
            
            //int min_x = MIN(m_sQuad.tl.vertices.x, m_sQuad.bl.vertices.x)
            tmp_rect[0] =   tmp_rect[0]==RECT_DEFAULT_VALUE || tmp_rect[0]>min_x ? min_x:tmp_rect[0];
            tmp_rect[1] =   tmp_rect[1]==RECT_DEFAULT_VALUE || tmp_rect[1]>min_y ? min_y:tmp_rect[1];
            tmp_rect[2] =   tmp_rect[2]==RECT_DEFAULT_VALUE || tmp_rect[2]<max_x ? max_x:tmp_rect[2];
            tmp_rect[3] =   tmp_rect[3]==RECT_DEFAULT_VALUE || tmp_rect[3]<max_y ? max_y:tmp_rect[3];

        }
        

    }
    if (_bRectNeedUpdate) {
        memcpy(_rect, tmp_rect, sizeof(tmp_rect));
        _bRectNeedUpdate = false;
    }
}

#if CC_SPRITE_DEBUG_DRAW == 1

void SpriterNode::draw() {
    float x = 0;//-this->getPosition().x;
    float y = 0;//-this->getPosition().y;
    
    // draw bounding box
    CCPoint vertices[4]={
        ccp(_rect[0]-x, _rect[1]-y),
        ccp(_rect[0]-x, _rect[3]-y),
        ccp(_rect[2]-x, _rect[3]-y),
        ccp(_rect[2]-x, _rect[1]-y),
    };
    ccDrawPoly(vertices, 4, true);
    
}
#endif

double SpriterNode::interpolate(double a, double b, double f)
{
    if (f == INFINITY) { f = 0.0; }
    if (f < 0) { f = 0.0; }
    if (f > 1) { f = 1.0; }
    if (f == NAN) { f = 1.0; }
    return a+(b-a)*f;
}

double SpriterNode::getPlaybackSpeed()
{
    return _playbackSpeed;
}

void SpriterNode::setPlaybackSpeed(double pSpeed)
{
    if(pSpeed != _playbackSpeed)
        _playbackSpeed = pSpeed;
}

bool SpriterNode::getIsFlipX()
{
    return _isFlipX;
}

void SpriterNode::setIsFlipX(bool b)
{
    if(b != _isFlipX)
        _isFlipX = b;
}